import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { JwtUtils } from '../01_auth/jwt.utils';
import { SysDept } from './entities/dept.entity';
import { generator } from '../../common/utils/generator';
import { Result } from '../../common/result/Result';
import { DeptForm } from './dto/dept.form';
import { ResultCode } from '../../common/result/ResultCode';
import { SystemConstant } from '../../common/constant/SystemConstant';
import { StatusEnum } from '../../common/enums/StatusEnum';
import { JwtPayload } from '../01_auth/jwt.payload';

@Injectable()
export class DeptService {
  constructor(
    @InjectRepository(SysDept)
    private deptRepository: Repository<SysDept>,
    private jwt: JwtUtils,
  ) {}
  async listDepartments(keywords: string, status: number) {
    // 1. 查询出所有的菜单
    const queryBuilder = await this.deptRepository.createQueryBuilder('su');
    if (keywords) {
      queryBuilder.andWhere('(su.name LIKE :keywords)', {
        keywords: `%${keywords}%`,
      });
    }
    if (status) {
      queryBuilder.andWhere('su.status = :status', {
        status: status,
      });
    }
    const depts = await queryBuilder.getMany();
    // 获取所有部门ID
    const ids: Set<number> = new Set();
    for (const dept of depts) {
      ids.add(dept.id);
    }
    // 获取父节点ID
    const parentIds: Set<number> = new Set();
    for (const dept of depts) {
      parentIds.add(dept.parentId);
    }
    // 获取根节点ID（递归的起点），即父节点ID中不包含在部门ID中的节点，注意这里不能拿顶级部门 O 作为根节点，因为部门筛选的时候 O 会被过滤掉
    const parentIdsArray = Array.from(parentIds);
    // 3. 获取根节点ID（递归的起点），即父节点ID中不包含在部门ID中的节点，
    //    注意这里不能拿顶级菜单 O 作为根节点，因为菜单筛选的时候 O 会被过滤掉
    const rootIds: number[] = [];
    for (const id of parentIdsArray) {
      if (!ids.has(id)) {
        rootIds.push(id);
      }
    }
    // 格式化数据
    const deptVOs = [];
    for (const dept of depts) {
      const deptVO = {
        id: dept.id,
        parentId: dept.parentId,
        name: dept.name,
        sort: dept.sort,
        status: dept.status,
        children: [],
        createTime: dept.createTime,
        updateTime: dept.updateTime,
      };
      deptVOs.push(deptVO);
    }
    // 4. 递归生成部门树形列表
    // const result = [];
    // for (const rootId of rootIds) {
    //   const res = generator.buildTree(deptVOs, rootId);
    //   res.forEach((r) => {
    //     result.push(r);
    //   });
    // }
    const result = generator.buildTree(deptVOs, 0);
    console.log(result);
    return Result.success(result);
  }
  async addDepartment(form: DeptForm, currentUser: JwtPayload) {
    // 1. 检验部门名是否存在
    const flag = await this.deptRepository.existsBy({ name: form.name });
    if (flag) {
      return ResultCode.DEPT_NAME_EXIST;
    }
    // 2. form->entity
    const dept = new SysDept({
      createBy: currentUser.userId,
      updateBy: currentUser.userId,
    });
    dept.parentId = form.parentId;
    dept.name = form.name;
    dept.sort = form.sort;
    dept.status = form.status;
    // 3. 生成部门路径(tree_path)，格式：父节点tree_path + , + 父节点ID，用于删除部门时级联删除子部门
    dept.treePath = await this.generatorTreePath(form.parentId);
    // 4. 保存部门并返回部门ID
    await this.deptRepository.save(dept);
    return Result.success();
  }
  async generatorTreePath(parentId: number) {
    if (SystemConstant.ROOT_NODE_ID == parentId) {
      return String(parentId);
    } else {
      const parent = await this.deptRepository.findOneBy({ id: parentId });
      return parent != null ? parent.treePath + ',' + parent.id : null;
    }
  }
  async listDeptOptions() {
    // 1. 获取所有的内容
    const depts = await this.deptRepository.findBy({
      status: StatusEnum.ENABLE.value,
    });
    // 2. 获取所有的菜单ID
    const deptIds: Set<number> = new Set();
    depts.forEach((dept) => {
      deptIds.add(dept.id);
    });
    // 3. 获取所有父级ID
    const parentIds: Set<number> = new Set();
    depts.forEach((dept) => {
      parentIds.add(dept.parentId);
    });
    const parentIdsArray = Array.from(parentIds);
    // 3. 获取根节点ID（递归的起点），即父节点ID中不包含在部门ID中的节点，
    // 注意这里不能拿顶级菜单 O 作为根节点，因为菜单筛选的时候 O 会被过滤掉
    const rootIds: number[] = [];
    for (const id of parentIdsArray) {
      if (!deptIds.has(id)) {
        rootIds.push(id);
      }
    }
    // 4. 格式化数据
    const deptVOs = [];
    for (const dept of depts) {
      const deptVO = {
        id: dept.id,
        parentId: dept.parentId,
        name: dept.name,
        label: dept.name,
        value: dept.id,
        children: [],
      };
      deptVOs.push(deptVO);
    }
    // 4. 使用递归函数来构建菜单树
    const result = [];
    for (const rootId of rootIds) {
      const res = generator.buildTree(deptVOs, rootId);
      res.forEach((r) => {
        result.push(r);
      });
    }
    return Result.success(result);
  }

  async updateDept(id: number, form: DeptForm, currentUser: JwtPayload) {
    // 1. 检验部门名是否存在
    const flag = await this.deptRepository
      .createQueryBuilder('dept')
      .where('dept.name = :name', { name: form.name })
      .andWhere('dept.id != :id', { id: id })
      .getCount()
      .then((count) => count > 0);
    if (flag) {
      return ResultCode.DEPT_NAME_EXIST;
    }
    // 2. form->entity
    const dept = new SysDept({
      updateBy: currentUser.userId,
    });
    dept.parentId = form.parentId;
    dept.name = form.name;
    dept.sort = form.sort;
    dept.status = form.status;
    // 3. 生成部门路径(tree_path)，格式：父节点tree_path + , + 父节点ID，用于删除部门时级联删除子部门
    dept.treePath = await this.generatorTreePath(form.parentId);
    // 4. 保存部门并返回部门ID
    await this.deptRepository.update({ id: id }, dept);
    return Result.success(dept.id);
  }

  async deleteDept(ids: string, currentUser: JwtPayload) {
    const idList = ids.split(',');
    // todo 判断该部门还有有没有人，如果有人则不能删除部门
    for (const id of idList) {
      await this.deptRepository
        .createQueryBuilder('u')
        .update()
        .set({
          deletedTime: new Date(),
          updateBy: currentUser.userId,
          updateTime: new Date(),
        })
        .where(
          `id = :id or CONCAT (',',tree_path,',') LIKE CONCAT('%,',:id,',%')`,
          { id: id },
        )
        .execute();
    }
    return Result.success();
  }

  async getForm(id: number) {
    const result = await this.deptRepository.findOneBy({ id: id });
    return Result.success(result);
  }
}
